#!/u01/dbaperl/bin/perl -w
# (C) 2007-2012 Alibaba Group Holding Limited

# Authors:
# thomas.wangyj@alibaba-inc.com

#  Copyright (C) 2012 Taobao Co.,Ltd.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#  Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use strict;
use warnings FATAL => 'all';

use Carp qw(croak);
use MHA::BinlogManager;
use MHA::BinlogPosFindManager;
use MHA::BinlogPosFinder;
use MHA::BinlogPosFinderXid;
use MHA::BinlogPosFinderElp;
use MHA::NodeConst;
use MHA::NodeUtil;
use MHA::SlaveUtil;
use Getopt::Long;
use Pod::Usage;
use File::Basename;
use File::Copy;
use Sys::Hostname;
use DBI;

use constant Show_Slave_Status_SQL       => "SHOW SLAVE STATUS";
use constant Master_Host                 => "Master_Host";
use constant Master_Log_File             => "Master_Log_File";
use constant Read_Master_Log_Pos         => "Read_Master_Log_Pos";
use constant Relay_Log_File              => "Relay_Log_File";
use constant Relay_Log_Pos               => "Relay_Log_Pos";
use constant Relay_Master_Log_File       => "Relay_Master_Log_File";
use constant Exec_Master_Log_Pos         => "Exec_Master_Log_Pos";

use constant Server_Id_SQL	         => "select \@\@global.server_id as Value";
use constant Relay_Log_SQL               => "select \@\@global.relay_log as Value";
use constant Disable_Relay_Purge_SQL     => "SET GLOBAL relay_log_purge=0";
use constant Log_Dir                     => "/u01/mysql/log";
our @slave_ip                            =  ();
our @slave_port                          =  ();

GetOptions(
  \my %opt, qw/
    help
    version
    slave1=s
    slave2=s
    slave3=s
    slave4=s
    slave5=s
    /,
) or pod2usage(2);

####################### option help

if ( $opt{help} ) {
  pod2usage(2);
}

if ( $opt{version} ) {
  print "diff_apply_logs version 1.0 \n";
  exit 0;
}

####################### option handle

if ( $opt{slave1} ) {
  my $slave = $opt{slave1};

  if ( $slave =~ m/(\S+)\^(\w+)/ ) {
    $slave_ip[0]   = $1;
    $slave_port[0] = $2;
  }
  else {
    croak "Option slave1 is error!\n";
  }
} 


if ( $opt{slave2} ) {
  my $slave = $opt{slave2};

  if ( $slave =~ m/(\S+)\^(\w+)/ ) {
    $slave_ip[1]   = $1;
    $slave_port[1] = $2;
  }
  else {
    croak "Option slave2 is error!\n";
  }
}

if ( $opt{slave3} ) {
  my $slave = $opt{slave3};

  if ( $slave =~ m/(\S+)\^(\w+)/ ) {
    $slave_ip[2]   = $1;
    $slave_port[2] = $2;
  }
  else {
    croak "Option slave3 is error!\n";
  }
}

if ( $opt{slave4} ) {
  my $slave = $opt{slave4};

  if ( $slave =~ m/(\S+)\^(\w+)/ ) {
    $slave_ip[3]   = $1;
    $slave_port[3] = $2;
  }
  else {
    croak "Option slave4 is error!\n";
  }
}

if ( $opt{slave5} ) {
  my $slave = $opt{slave5};

  if ( $slave =~ m/(\S+)\^(\w+)/ ) {
    $slave_ip[4]   = $1;
    $slave_port[4] = $2;
  }
  else {
    croak "Option slave5 is error!\n";
  }
}

my $slave_cnt = keys %opt;

if ( $slave_cnt < 2 ) {
  croak "Option slave number is not enough.There must be two slaves!\n";
}

&main();

####################### get slave_position

sub get_dbh () {
  my $ip      = shift;
  my $port    = shift;
  my $db_name = shift;

  $db_name = "test" if ( !defined($db_name) );
  $port    = "3306" if ( !defined($port) );
  $ip      = "127.0.0.1" if ( !defined($ip) );

  my $dsn = "DBI:mysql:$db_name:$ip:$port"; 
  my $dbh = DBI->connect($dsn,"slave","slave",{RaiseError => 1});

  return $dbh;
} 

sub get_slave_status ($) {
  my $dbh   = shift;
  my ( $query,$sth,$ret,$href );
  my %status = (); 

  $query = Show_Slave_Status_SQL;
  $sth = $dbh->prepare($query);
  $ret = $sth->execute();
   
  #return $ret;

  if ( !defined($ret) || $ret != 1 ) {
    croak "Can not get slave status!\n"; 
  }
  elsif ( defined( $sth->errstr ) ) {
    croak "Got error when executing slave status, the error is ",$sth->errstr," \n";
  }

  $href = $sth->fetchrow_hashref;

  for my $key (
    Master_Host,         Master_Log_File,
    Read_Master_Log_Pos, Relay_Log_File,
    Relay_Log_Pos,       Relay_Master_Log_File,
    Exec_Master_Log_Pos
    )
  {
    $status{$key} = $href->{$key};
  }
  
  return %status;
  
}

sub is_server_id($) {
  my $dbh = shift;
  return MHA::SlaveUtil::get_variable($dbh,Server_Id_SQL);
}

####################### get max slave

sub get_max_slave() {
  my $dbh;
  my $max_log_number      =  0;
  my $max_log_pos         =  0;
  my $max_slave_ip            ;
  my $max_slave_port          ;
  my %status              = ();
  my @master_log_file     = ();
  my @read_master_log_pos = ();  

  for my $i (0 ..$#slave_ip ) {
    if ( defined($slave_ip[$i]) and defined($slave_port[$i]) ) {
      
      $dbh                        = &get_dbh($slave_ip[$i],$slave_port[$i]);
      %status                     = get_slave_status($dbh);
      $master_log_file[$i]        = $status{Master_Log_File};
      $read_master_log_pos[$i]    = $status{Read_Master_Log_Pos} + 0;
      my( $log_head,$log_number ) = MHA::BinlogManager::get_head_and_number($status{Master_Log_File});
      $log_number                 = $log_number + 0;

      if ( $log_number > $max_log_number ) {
        $max_log_number = $log_number;
        $max_log_pos    = $read_master_log_pos[$i];
        $max_slave_ip   = $slave_ip[$i];
        $max_slave_port = $slave_port[$i];
      }
      elsif ( $log_number == $max_log_number ) {
        
        if ( $read_master_log_pos[$i] > $max_log_pos ) {
          $max_log_pos    = $read_master_log_pos[$i];
          $max_slave_ip   = $slave_ip[$i];
          $max_slave_port = $slave_port[$i];
        }         

      }

      #print $log_number,"\n"; 
      #print $master_log_file[$i],"  ",$read_master_log_pos[$i]," \n";
      #print $slave_ip[$i],"  ",$slave_port[$i],"\n";

    }

  }
  return $max_slave_ip,$max_slave_port,$max_log_number,$max_log_pos;  
}


####################### get max slave server id
sub get_max_slave_serv_id($) {
  my $dbh = shift;
  my ( $query,$sth,$ret,$href );
  $query  = Server_Id_SQL;
  $sth    = $dbh->prepare($query);
  $ret    = $sth->execute();

  if ( !defined($ret) || $ret != 1 ) {
    croak "Can not get max slave id!\n"; 
  }
  elsif ( defined( $sth->errstr ) ) {
    croak "Got error when executing server id, the error is ",$sth->errstr," \n";
  }
  
  $href = $sth->fetchrow_hashref;
  
  return $href->{Value};
}


####################### get max slave dir and prefix
sub get_max_slave_dirpre($) {
  my $dbh = shift;
  my ( $query,$sth,$ret,$href );
  $query  = Relay_Log_SQL;
  $sth    = $dbh->prepare($query);
  $ret    = $sth->execute();
  
  if ( !defined($ret) || $ret != 1 ) {
    croak "Can not get max slave relay dir!\n";
  }
  elsif ( defined( $sth->errstr ) ) {
    croak "Got error when executing relay dir, the error is ",$sth->errstr," \n";
  }

  $href = $sth->fetchrow_hashref;

  return dirname($href->{Value}),basename($href->{Value});
}


####################### disable relay purge 
sub disable_relay_purge($) {
  my $dbh = shift;
  $dbh->do(Disable_Relay_Purge_SQL);
}


####################### output for API

sub main()
{
  my %max_slave = ();
  my $max_log_number;
  my $max_log_pos;
  my ( $hash_ip,$hash_port,$hash_pos );
  ( $max_slave{h_slaveip},$max_slave{h_slaveport},$max_log_number,$max_log_pos ) = get_max_slave();
  
  my $dbh               = &get_dbh($max_slave{h_slaveip},$max_slave{h_slaveport});
  $max_slave{h_slaveid} = get_max_slave_serv_id($dbh);
  ( $max_slave{h_slavedir},$max_slave{h_slaveprefix} ) = get_max_slave_dirpre($dbh);
  disable_relay_purge($dbh);

  my %low_slave = ();
  my %status    = ();

  for my $i (0 ..$#slave_ip ) {
    if ( defined($slave_ip[$i]) and defined($slave_port[$i]) and ($max_slave{h_slaveip} ne $slave_ip[$i]) ) {
      
      $dbh                        = &get_dbh($slave_ip[$i],$slave_port[$i]);
      %status                     = get_slave_status($dbh);
      my( $log_head,$log_number ) = MHA::BinlogManager::get_head_and_number($status{Master_Log_File});
      $log_number                 = $log_number + 0; 
      if ( $max_log_number > $log_number ) {
        print "The relay log has been purge,so the slave $slave_ip[$i] can not recovery.\n";
      } 
      elsif ( $max_log_pos > $status{Read_Master_Log_Pos} + 0  ) {

        $hash_ip   = "l_slaveip_" . $i;
        $hash_port = "l_slaveport_" . $i;
        $hash_pos  = "l_slavepos_" . $i;
        $low_slave{$hash_ip}   = $slave_ip[$i];
        $low_slave{$hash_port} = $slave_port[$i];
        $low_slave{$hash_pos}  = $status{Read_Master_Log_Pos};
      } 
      else {
        print "The slave $slave_ip[$i] not need recovery.\n";
      }
    }
   
  }

  return %max_slave,%low_slave;
  
}


# ############################################################################
# Documentation
# ############################################################################

=pod

=head1 NAME

get_max_slave is a cool tool for Mysql.

=head1 SYNOPSIS

get_max_slave --slave1=s --slave2=s --slave3=s --slave4=s --slave5=s
